package com.xinting.piano.service;

import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.AsyncTask;
import android.util.Log;

import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import com.xinting.piano.data.PApp;
import com.xinting.piano.data.PError;
import com.xinting.piano.data.PMelody;
import com.xinting.piano.data.PUser;
import com.xinting.piano.lib.audio.PAudio;
import com.xinting.piano.lib.network.PNetWork;
import com.xinting.piano.lib.storage.PSQLite;
import com.xinting.piano.lib.storage.PSharePreferences;
import com.xinting.piano.lib.util.PUtil;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wuxinting on 15/11/7.
 * 录音、试听、播放、上传相关操作，更新新的分享，业务
 */
public class PAlbum {

    public static final String LOG_TAG = PApp.LOG_TAG+"_album";

    //album 文件
    private File file;
    private Status status;
    //判断是否有更多的历史乐曲，如果返回不满十条表示没有
    private boolean hasMore = true;

    //一次正常返回的乐曲数量
    private static final int MELODY_TRUNK = 10;

    //public static define
    public static final String URL_UPLOAD = "album/upload";
    public static final String URL_MELODIES = "album/melodies";
    public static final String URL_MELODY = "album/melody";
    public static final String URL_LIKE = "album/like";

    //与server交互的日期格式
    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";

    private IPlayComplete playComplete;
    private PAudio.IOnStatusChangeListener statusChangeListener;

    /**
     * 状态枚举
     */
    public enum Status {
        NORMAL, //没有动作
        RECORD, //正在录音
        TRY_PLAY, //正在试听
        PLAY, //正在播放
        PAUSE, //暂停
        UPLOAD, //正在上传
        DOWNLOAD, //正在下载
        UPDATE, //正在更新
        MORE, //正在加载更多
        STOP, //停止
    }

    /**
     * 获取当前album的状态
     * @return
     */
    public Status getStatus() {
        return status;
    }

    /**
     * 设置状态，只可以在业务层改变，不可以在UI层改变
     * @param status
     */
    private void setStatus(Status status) {
        this.status = status;
    }

    /**
     * 是否有更多乐曲（历史）
     * @return true | false
     */
    public boolean isHasMore() {
        return hasMore;
    }

    /**
     * 设置是否有更多乐曲（历史）,如果server返回不满十条表示没有更多
     * @param hasMore true | false
     */
    public void setHasMore(boolean hasMore) {
        this.hasMore = hasMore;
    }

    /**
     * 构造函数，传入文件名，该文件用于存储录音目标文件、试听源文件、播放源文件、上传的源文件
     * 如果file为空，表示更新乐曲，而不会有其它业务操作
     * @param file
     */
    public PAlbum(File file) {
        this.file = file;
        statusChangeListener = new OnStatusChangeListener();
    }

    public IPlayComplete getPlayComplete() {
        return playComplete;
    }

    public void setPlayComplete(IPlayComplete playComplete) {
        this.playComplete = playComplete;
    }

    /**
     * 获取用户user发布的乐曲列表
     * <font color="red">约定，前面有+号，表示获取最新，-号表示获取更多（历史）</font>
     * @param user 需要乐曲列表的用户, 为空时不指定用户
     * @param newest 获取id newest之后，最新的乐曲列表（id比newest还大)
     * @param handleJson 回调函数
     */
    public void updateMelodies(PUser user, Date newest, IHandleJson handleJson) {
        String url = PUtil.joinURL(PApp.URL_SERVER, URL_MELODIES);
        Map<String, String> params = new HashMap<>();
        PAccount.getInstance().fillTokenToParam(params);

        if (newest != null) {
            params.put(PApp.KEY_MORE, "+" + new SimpleDateFormat(dateFormat).format(newest));
        }
        if (user != null) {
            params.put(PUser.KEY, user.getId());
        }

        Log.v(LOG_TAG, params.toString());
        try {
            PNetWork.getInstance().post(url, params, new HandleJson(handleJson));
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage());
        }

        setStatus(Status.UPDATE);
    }

    /**
     * 获取更多的乐曲
     * @param user 指定某个用户的乐曲，为空表示当前用户
     * @param oldest 目前最老的一个乐曲ID
     * @param handleJson 回调
     */
    public void moreMelodies(PUser user, Date oldest, IHandleJson handleJson) {
        String url = PUtil.joinURL(PApp.URL_SERVER, URL_MELODIES);
        Map<String, String> params = new HashMap<>();
        PAccount.getInstance().fillTokenToParam(params);

        if (oldest != null) {
            params.put(PApp.KEY_MORE, "-" + new SimpleDateFormat(dateFormat).format(oldest));
        }
        if (user != null) {
            params.put(PUser.KEY, user.getId());
        }

        Log.v(LOG_TAG, params.toString());
        try {
            PNetWork.getInstance().post(url, params, new HandleJson(handleJson));
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage());
        }

        setStatus(Status.MORE);
    }

    /**
     * download melody, by id
     * 返回的json中如果只有process key,表示为进度
     * 如果有code == 0，表示已经下载完成
     * @param handler
     */
    public void downloadMelody(String id, IHandleJson handler) {
        if (file == null) {
            Log.e(LOG_TAG, "file is null");
            return;
        }

        String url = PUtil.joinURL(PApp.URL_SERVER, URL_MELODY);
        Map<String, String> params = new HashMap<>();
        params.put(PApp.KEY_ID, id);
        PAccount.getInstance().fillTokenToParam(params);
        new DownloadAsyncTask(url, params, handler).execute();

        setStatus(Status.DOWNLOAD);
    }

    /**
     * 保存乐曲
     * @param melody
     * @return
     */
    public boolean saveMelody(PMelody melody) {
        if (melody == null) {
            return false;
        }

        PSQLite lite = new PSQLite(PApp.getAppContext());
        lite.selectTable(PMelody.TABLE_NAME, melody.getDBDesc());
        return lite.insertData(melody.toContentValues()) != 0;
    }

    /**
     * 保存乐曲列表
     * @param melodies
     * @return
     */
    public boolean saveMelodies(List<PMelody> melodies) {
        if (melodies == null || melodies.size() < 1) {
            return false;
        }

        PSQLite lite = new PSQLite(PApp.getAppContext());
        lite.selectTable(PMelody.TABLE_NAME, melodies.get(0).getDBDesc());
        for (PMelody melody : melodies) {
            lite.insertData(melody.toContentValues());
        }
        return true;
    }

    /**
     * 从本地加载乐曲
     * @param melodies
     * @return
     */
    public boolean loadMelodies(List<PMelody> melodies) {
        if (melodies == null) {
            return false;
        }

        PSQLite lite = new PSQLite(PApp.getAppContext());
        try {
            Cursor c = lite.query("select * from " + PMelody.TABLE_NAME, null);
            while (c.moveToNext()) {
                PMelody melody = new PMelody();
                melody.fromCursor(c);

                melodies.add(melody);
            }
        } catch (SQLiteException e) {
            Log.w(LOG_TAG, "No melodies local. " + e.toString());
        }
        return true;
    }

    /**
     * 删除乐曲表
     */
    public void dropMelodyTable() {
        PSQLite lite = new PSQLite(PApp.getAppContext());
        lite.selectTable(PMelody.TABLE_NAME, null);
        lite.dropTable();
    }

    /**
     * 喜欢某个melody
     * @param melody
     * @return
     */
    public boolean likeMelody(PMelody melody, IHandleJson handleJson) {
        /**
         * 如果已经喜欢过，则返回错误
         */
        PSharePreferences sp = new PSharePreferences(PApp.getAppContext());
        String likes = sp.get(PApp.KEY_SP_LIKE, "");
        String[] melodies = likes.split(";");
        if (Arrays.asList(melodies).contains(melody.getId())) {
            if (handleJson != null) {
                JSONObject json = new JSONObject();
                try {
                    json.put(PError.KEY_CODE, PError.ERR_REPEAT);
                    handleJson.handleJson(json);
                } catch (JSONException e) {
                    Log.e(LOG_TAG, "Put repeat like fail.");
                }
            }
            return false;
        }

        /**
         * 如果没有喜欢过，则发送到server喜欢该乐曲
         */
        String url = PUtil.joinURL(PApp.URL_SERVER, URL_LIKE);
        Map<String, String> params = new HashMap<>();
        PAccount.getInstance().fillTokenToParam(params);
        params.put(PMelody.KEY, melody.getId());

        try {
            PNetWork.getInstance().post(url, params, new HandleJson(handleJson));
        } catch (IOException e) {
            Log.e(LOG_TAG, e.toString());
            return false;
        }

        /**
         * 保存到本地
         */
        likes += melody.getId() + ";";
        sp.save(PApp.KEY_SP_LIKE, likes);

        return true;
    }

    /**
     * 录音，写入文件file
     * @return
     */
    public boolean record() {
        if (file == null) {
            return false;
        }

        try {
            PAudio.getInstance().record(file.getAbsolutePath());
            setStatus(Status.RECORD);
        } catch (IOException e) {
            Log.e(LOG_TAG, e.toString());
            return false;
        }
        return true;
    }

    /**
     * 播放 file
     * @return
     */
    public boolean play() {
        if (file == null) {
            return false;
        }

        try {
            PAudio audio = PAudio.getInstance();
            audio.setPath(file.getAbsolutePath(), true);
            audio.play();

            audio.setOnStatusChangeListener(statusChangeListener);
            setStatus(Status.PLAY);
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * upload file
     * @param melody 该音频的信息
     * @return
     */
    public boolean upload(PMelody melody, PNetWork.IProgress progress) {
        if (file == null) {
            return false;
        }

        String url = PUtil.joinURL(PApp.URL_SERVER, URL_UPLOAD);
        Map<String, String> params = new HashMap<>();
        PAccount.getInstance().fillTokenToParam(params);

        try {
            String title = URLEncoder.encode(melody.getTitle(), "UTF-8");
            params.put(PMelody.KEY_TITLE, title);
        } catch (UnsupportedEncodingException e) {
            Log.e(LOG_TAG, e.getMessage());
            return false;
        }
        params.put(PMelody.KEY_DURATION, ""+melody.getDuration());
        new UploadAsyncTask(url, params, progress).execute(file);

        setStatus(Status.UPLOAD);
        return true;
    }

    /**
     * 试听，源文件file
     * @return
     */
    public boolean tryPlay() {
        setStatus(Status.TRY_PLAY);
        return play();
    }

    /**
     * 暂停播放 file
     * @return
     */
    public boolean pausePlay() {
        PAudio.getInstance().pausePlay();

        setStatus(Status.PAUSE);
        return true;
    }

    /**
     * 恢复播放
     * @return
     */
    public boolean resumePlay() {
        PAudio.getInstance().resumePlay();

        setStatus(Status.PLAY);
        return true;
    }

    /**
     * 停止录音
     * @return
     */
    public boolean stopRecord() {
        PAudio.getInstance().stopRecord();

        setStatus(Status.STOP);
        return true;
    }

    /**
     * 播放完成的回调接口
     */
    public interface IPlayComplete {

        void onCompletion();
    }

    /**
     * audio库的状态改变
     */
    private class OnStatusChangeListener implements PAudio.IOnStatusChangeListener {

        @Override
        public void onStatusChange(PAudio.Status before, PAudio.Status cur) {
            if (cur == PAudio.Status.STOP && playComplete != null) {
                playComplete.onCompletion();
            }
        }
    }
    /**
     * async upload task
     */
    private class UploadAsyncTask extends AsyncTask<File, Long, String> {

        private String url;
        private Map<String, String> params;
        private PNetWork.IProgress progress;

        public UploadAsyncTask(String url, Map<String, String> params, PNetWork.IProgress progress) {
            this.url = url;
            this.params = params;
            this.progress = progress;
        }

        @Override
        protected void onProgressUpdate(Long... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected String doInBackground(File... files) {
            try {
                return PNetWork.getInstance().postFile(url, file, params, progress);
            } catch (Exception e) {
                return "";
            }
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            if (!s.isEmpty()) {
                progress.onProgress(file.length(), file.length());
            } else {
            }
            setStatus(PAlbum.Status.NORMAL);
            Log.v(LOG_TAG, s);
        }

    }

    /**
     * download async task.
     */
    private class DownloadAsyncTask extends AsyncTask<Void, Long, JSONObject> {

        private Map<String, String> params;
        private String url;
        private IHandleJson handler;

        public DownloadAsyncTask(String url, Map<String, String> params, IHandleJson handler) {
            this.params = params;
            this.url = url;
            this.handler = handler;
        }

        @Override
        protected void onProgressUpdate(Long... values) {
            JSONObject json = new JSONObject();
            try {
                json.put(PApp.KEY_PROCESS, true);
                json.put(PApp.KEY_PROCESS_AT, values[0]);
                json.put(PApp.KEY_PROCESS_RANGE, values[1]);
            } catch (JSONException e) {
                Log.e(LOG_TAG, e.getMessage());
            }

            if (handler != null) {
                handler.handleJson(json);
            }
        }

        @Override
        protected void onPostExecute(JSONObject jsonObject) {
            setStatus(PAlbum.Status.NORMAL);

            if (handler != null) {
                handler.handleJson(jsonObject);
            }
        }

        @Override
        protected JSONObject doInBackground(Void... voids) {
            try {
                Log.v(LOG_TAG, "downloading..");
                Response response = PNetWork.getInstance().postSync(url, params);
                ResponseBody body = response.body();

                long len = body.contentLength();
                InputStream is = body.byteStream();

                byte[] buffer = new byte[1024];
                long downed = 0; //已经下载的量
                int count = 0;

                FileOutputStream fos = new FileOutputStream(file);
                while ((count = is.read(buffer, 0, 1024)) > 0) {
                    fos.write(buffer, 0, count);

                    downed += count;
                    publishProgress(downed, len);
                }
                fos.flush();
                is.close();
                fos.close();

                JSONObject json = new JSONObject();
                json.put(PApp.ERR_CODE, PApp.ERR_SUCCESS);
                return json;
            } catch (IOException e) {
                Log.e(LOG_TAG, e.getMessage());
                return null;
            } catch (JSONException e) {
                Log.e(LOG_TAG, e.getMessage());
                return null;
            }
        }
    }

    /**
     * 统一处理网络库返回的信息
     */
    private class HandleJson implements PNetWork.IHandleHttpResult {

        private final IHandleJson handleJson;

        public HandleJson(IHandleJson handleJson) {
            this.handleJson = handleJson;
        }

        @Override
        public void onRequest(Request req, IOException e) {
            Log.e(PApp.LOG_TAG, "error");
            PBroadcast.getInstance().broadcast(PApp.BC_NETWORK_ERROR, null);

            if (handleJson != null) {
                handleJson.handleJson(null);
            }

            setStatus(Status.NORMAL);
        }

        @Override
        public void onResponse(Response res) throws IOException {
            JSONObject json = null;
            long length = res.body().contentLength();
            Log.w(LOG_TAG, "Response length "+ length);

            try {
                json = new JSONObject(res.body().string());
                Log.v(LOG_TAG, json.toString());

                //判断是否有更多
                if (getStatus() == Status.MORE) {
                    JSONArray melodies = json.optJSONArray(PMelody.KEY_S);
                    if (melodies != null && melodies.length() < MELODY_TRUNK) {
                        setHasMore(false);
                    }
                }

                if (handleJson != null) {
                    handleJson.handleJson(json);
                }

                setStatus(Status.NORMAL);
            } catch (JSONException e) {
                Log.e(LOG_TAG, e.toString());
            }

        }
    }
}
